音频组件emits事件定义(切换音频)
概述
本节定义音频组件的 defineEmits 事件,实现子组件向父组件的通知机制,主要用于播放列表场景中的音频切换和播放状态同步。
Emits 事件设计
事件类型定义
// types.ts
export interface AudioPlayerEmits {
/** 播放状态变化 */
'update:playing': [playing: boolean]
/** 音频播放结束 */
ended: []
/** 请求播放上一曲 */
prev: []
/** 请求播放下一曲 */
next: []
/** 播放模式变化 */
'update:loopMode': [mode: number]
/** 音量变化 */
'update:volume': [volume: number]
/** 速率变化 */
'update:rate': [rate: number]
/** 加载错误 */
error: [msg: string]
}
typescript
组件中定义 emits
// AudioPlayer.vue
const emit = defineEmits<AudioPlayerEmits>()
// 在 Howl 事件回调中触发 emit
function initAudio(src: string | string[]) {
audioInstance.value = new Howl({
src: Array.isArray(src) ? src : [src],
onplay() {
emit('update:playing', true)
},
onpause() {
emit('update:playing', false)
},
onstop() {
emit('update:playing', false)
},
onend() {
emit('update:playing', false)
emit('ended') // 通知父组件:当前曲目播放结束
},
onloaderror(_, error) {
emit('error', `加载失败: ${error}`)
}
})
}
// 上一曲/下一曲按钮触发 emit
function handlePrev() {
emit('prev') // 由父组件决定切换到哪首歌
}
function handleNext() {
emit('next')
}
typescript
父组件处理播放列表切换
<template>
<AudioPlayer
ref="playerRef"
:src="currentTrack.src"
:list="playlist"
@prev="handlePrev"
@next="handleNext"
@ended="handleEnded"
@update:playing="playing = $event"
@error="handleError"
/>
<!-- 播放列表 -->
<div class="playlist">
<div
v-for="(track, index) in playlist"
:key="index"
:class="{ active: index === currentIndex }"
@click="playTrack(index)"
>
{{ track.title }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import AudioPlayer from './AudioPlayer.vue'
import type { AudioListItem } from './types'
const playlist = ref<AudioListItem[]>([
{ src: 'song1.mp3', title: '歌曲 1' },
{ src: 'song2.mp3', title: '歌曲 2' },
{ src: 'song3.mp3', title: '歌曲 3' }
])
const currentIndex = ref(0)
const playing = ref(false)
const loopMode = ref(0) // 0: 顺序, 1: 列表循环, 2: 单曲循环, 3: 随机
const currentTrack = computed(() => playlist.value[currentIndex.value])
function playTrack(index: number) {
currentIndex.value = index
// src 变化后,watch 会重新初始化音频实例
}
function handlePrev() {
if (currentIndex.value > 0) {
currentIndex.value--
} else if (loopMode.value === 1) {
// 列表循环:回到最后一首
currentIndex.value = playlist.value.length - 1
}
}
function handleNext() {
if (currentIndex.value < playlist.value.length - 1) {
currentIndex.value++
} else if (loopMode.value === 1) {
// 列表循环:回到第一首
currentIndex.value = 0
}
}
function handleEnded() {
// 根据播放模式决定下一步
switch (loopMode.value) {
case 0: // 顺序播放:非最后一首时播放下一首
if (currentIndex.value < playlist.value.length - 1) {
handleNext()
}
break
case 1: // 列表循环
handleNext()
break
case 2: // 单曲循环:Howler.loop(true) 自动处理
break
case 3: // 随机播放
currentIndex.value = Math.floor(Math.random() * playlist.value.length)
break
}
}
function handleError(msg: string) {
console.error(msg)
// 可跳过错误曲目,自动播放下一首
handleNext()
}
</script>
vue
事件流向图
AudioPlayer (子组件) 父组件
───────────────── ─────────
[上一曲] ─── emit('prev') ──→ handlePrev() → 更新 currentIndex
[下一曲] ─── emit('next') ──→ handleNext() → 更新 currentIndex
[播放结束] ─ emit('ended') ─→ handleEnded() → 根据模式决定下一首
[播放状态] ─ emit('update:playing') ─→ 同步 playing 状态
[错误] ──── emit('error') ──→ handleError()
父组件更新 currentIndex
↓
currentTrack.src 变化
↓
watch(src) → 重新 initAudio
text
小结
defineEmits用于子组件向父组件发送事件通知- 上一曲/下一曲通过
emit委托给父组件处理,子组件不直接操作列表 - 父组件根据
loopMode在handleEnded中决定下一首的播放策略 - 错误事件可通过自动跳过当前曲目实现容错处理
↑